package org.jackysoft.interceptor;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jackysoft.annotations.ActionRequestMethod;
import org.jackysoft.annotations.MethodInceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.google.common.collect.Maps;

@Component
public class ProxyInterceptor extends HandlerInterceptorAdapter implements
		ApplicationContextAware {

	static final Log logger = LogFactory.getLog(ProxyInterceptor.class);

	private ApplicationContext appContext;

	public static final Map<String, AbstractInterceptor> inters = Maps
			.newConcurrentMap();

	static {
		try {
			registerInterceptor();
		} catch (ClassNotFoundException | InstantiationException
				| IllegalAccessException e) {
			logger.error(e);

		}
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		String methodName = handlerMethod.getMethod().getName();

		logger.info("Action was intercepted by " + methodName + " method");
		AbstractInterceptor interp = inters.get(methodName);
		if (interp == null) {
			logger.info("method : "
					+ methodName
					+ " was not maped by any interceptor, use default invoked execution ");
			return;
		}
		interp.setContext(appContext);
		interp.init(request, response, handlerMethod, modelAndView);
		interp.postHandle(request, response, handler, modelAndView);
		logger.info("Action was executed by " + interp.getClass().getName());

	}

	static final void registerInterceptor() throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
		ClassPathScanningCandidateComponentProvider cp = new ClassPathScanningCandidateComponentProvider(
				true);
		cp.addIncludeFilter(new TypeFilter() {

			@Override
			public boolean match(MetadataReader metadataReader,
					MetadataReaderFactory metadataReaderFactory)
					throws IOException {
				AnnotationMetadata am = metadataReader.getAnnotationMetadata();
				return am
						.isAnnotated("org.jackysoft.annotations.MethodInceptor");
			}

		});

		Set<BeanDefinition> bd = cp
				.findCandidateComponents("org/jackysoft/interceptor");

		for (BeanDefinition d : bd) {

			Class<?> cls = Class.forName(d.getBeanClassName());
			MethodInceptor mano = cls.getAnnotation(MethodInceptor.class);
			Object obj = cls.newInstance();
			AbstractInterceptor target = (AbstractInterceptor) obj;
			ActionRequestMethod[] methods = mano.method();
			for (ActionRequestMethod m : methods) {
				inters.put(m.name(), target);
				logger.info("Action method[" + m + "]->[" + cls.getName() + "]");
			}
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.appContext = applicationContext;

	}

}
